package org.jeecg.common.util.mall;

import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.utils.DateUtils;
import org.jboss.logging.Logger;

import java.util.*;

/**
 * @author wq
 * @date 2018/10/22
 * @description kpl util
 */
public class TbkUtil {

    private static Logger logger = Logger.getLogger(TbkUtil.class);
    private static String requestUrl;
    private static String ak;
    private static String sk;
    private static String site;
    private static String zone;

    private static String getVersion() {
        return "2.0";
    }
    private static void getResource() {
        if (requestUrl == null || ak == null || sk == null || site == null || zone == null) {
            requestUrl = SpringContextUtil.getEnvironmentProperty("tbk.url");
            ak = SpringContextUtil.getEnvironmentProperty("tbk.ak");
            sk = SpringContextUtil.getEnvironmentProperty("tbk.sk");
            site = SpringContextUtil.getEnvironmentProperty("tbk.site");
            zone = SpringContextUtil.getEnvironmentProperty("tbk.zone");
        }
    }

    public static String request(String method, Map<String, String> params) throws Exception {
        return request(method, getVersion(), params);
    }

    public static String request(String method, String version, Map<String, String> params) throws Exception {
        getResource();
        return request0(method, version, params, ak, sk, 3);
    }

    private static String request0(String method, String version, Map<String, String> params, String appKey, String appSecret, int retryTimes) throws Exception {
        boolean hasError = false;
        String errMsg = "";
        String res = null;
        try {
            Map<String, String> requestParams = getRequestParam(method, version, params, appKey, appSecret);
            RequestConfig requestConfig = RequestConfig.custom().setCookieSpec("mySpec").setSocketTimeout(1200).build();
            res ="";// HttpUtils.httpClientPost(requestUrl, null, requestParams, requestConfig);
        } catch (Exception custom) {
            logger.error("tbk request error: " + method + ":" + custom.getMessage());
            hasError = true;
            errMsg = custom.getMessage();
        }
        if (res != null) {
            Map<String, Object> map = JSONUtil.parseObj(res);
            boolean hasErrorResponse = map.containsKey("error_response");
            if (hasErrorResponse) {
                int oldRetryTimes = retryTimes;
                retryTimes = 0;
                hasError = true;
                errMsg = res;
                map = (Map<String, Object>) map.get("error_response");
                boolean hasSubCodeField = map.containsKey("sub_code") && map.get("sub_code") != null;
                if (hasSubCodeField) {
                    String subCode = map.get("sub_code").toString();
                    logger.error("tbk request has error: " + method + " :: " + subCode);
                    boolean flowOverLoad = "isv.flow-overload".equals(subCode);
                    if (flowOverLoad) {
                        logger.error("tbk request too fast: " + method);
                        retryTimes = oldRetryTimes;
                    }
                    boolean itemNotFound = "50001".equals(subCode) || "10000".equals(subCode);
                    if (itemNotFound) {
                        logger.error("item not found: " + method);
                    }
                    boolean searchParamError = "30002".equals(subCode);
                    if (searchParamError) {
                        logger.error("search param error: " + method);
                    }
                }
            }
        }

        if (hasError) {
            return requestError(method, version, params, appKey, appSecret, retryTimes, errMsg);
        }
        return res;
    }

    private static String requestError(String method, String version, Map<String, String> params, String appKey,
                                       String appSecret, int retryTimes, String errorMsg) throws Exception {
        if (retryTimes > 1) {
            logger.error("tbk request retry: " + retryTimes + " :: " + method);
            Thread.sleep(350);
            return request0(method, version, params, appKey, appSecret, retryTimes - 1);
        } else {
            throw new Exception(errorMsg);
        }
    }


    public static Map<String, String> sign(String method, Map<String, String> params) {
        return sign(method, getVersion(), params);
    }
    public static Map<String, String> sign(String method, String version, Map<String, String> params) {
        getResource();
        return getRequestParam(method, version, params, ak, sk);
    }

    private static Map<String, String> getRequestParam(String method, String version, Map<String, String> params, String appKey, String appSecret) {
        Map<String, String> requestParams = new TreeMap<>();
        requestParams.put("timestamp", DateUtil.now());
        requestParams.put("v", version);
        requestParams.put("sign_method", "md5");
        requestParams.put("format", "json");
        requestParams.put("method", method);
        requestParams.put("app_key", appKey);
        boolean noPid = !params.containsKey("adzone_id");
        if (noPid) {
            requestParams.put("adzone_id", zone);
        }
        requestParams.putAll(params);

        String signStr ="";// PayUtil.sign(requestParams, appSecret);

        requestParams.put("sign", signStr);
        logger.debug("sign param: " + requestParams);
        return requestParams;
    }

    public static Double queryCommission(String skuId) {
        if (skuId == null) {
            return 0d;
        } else {
            return null;
        }
    }

    /**
     * @param now  起始时间
     * @param unit 往后推几个单位 (一个单位20分钟)
     * @throws Exception e
     */
    private static Map<String, Map<String, Object>> oneHourQuery(Date now, int unit) throws Exception {
        Map<String, Map<String, Object>> orderInfoMap = new HashMap<>(100);
        for (int i = 0; i < unit; i++) {
            Date afterHour =  DateUtil.offsetMinute(now, ( 20 * i));
            orderInfoMap.putAll(getOrderInfoMap(afterHour, 1, "1", null));
            Thread.sleep(100);
        }
        return orderInfoMap;
    }

    public static Map<String, Map<String, Object>> getOrderInfoMap(String orderHour) throws Exception {
        Date hour = DateUtil.parse(orderHour, "yyyy-MM-dd HH");
        return getOrderInfoMap(hour);
    }

    public static Map<String, Map<String, Object>> getOrderInfoMap(Date hour) throws Exception {
        return oneHourQuery(hour, 3);
    }

    public static Map<String, Object> getOrderInfo(PayOrderModel orderModel) throws Exception {
        if (orderModel == null) {
            return null;
        }
        String orderScene = "";//orderModel.other("orderScene");
        String orderCountType ="";// orderModel.other("orderCountType");
        if (StringUtils.isBlank(orderScene)) {
            orderScene = "1";
        }
        if (StringUtils.isBlank(orderCountType)) {
            orderCountType = "2";
        }
        Date createTime = DateUtils.parseDate(orderModel.getCreateTime());
        return TbkUtil.getOrderInfo(createTime, orderModel.getOrderNo(), orderScene, orderCountType);
    }
    public static Map<String, Object> getOrderInfo(Date now, String orderNo, String orderScene, String orderCountType) throws Exception {
        Map<String, Map<String, Object>> orderInfoMap = getOrderInfoMap(now, 1, orderScene, orderCountType);
        return orderInfoMap.getOrDefault(orderNo, null);
    }
    public static Map<String, Object> getOrderInfo(Date now, String orderNo) throws Exception {
        Map<String, Map<String, Object>> orderInfoMap = getOrderInfoMap(now, 1);
        return getOrderInfo(now, orderNo, null, null);
    }

    public static Map<String, Map<String, Object>> getOrderInfoMap(Date now, int pageNo) throws Exception {
        return getOrderInfoMap(now, pageNo, null, null);
    }

    public static Map<String, Map<String, Object>> getOrderInfoMap(Date now, int pageNo, String orderScene, String orderCountType) throws Exception {
        return getOrderInfoMap(now, pageNo, orderScene, orderCountType, null);
    }
    public static Map<String, Map<String, Object>> getOrderInfoMap(Date now, int pageNo, String orderScene, String orderCountType, String span) throws Exception {
        Map<String, Map<String, Object>> orderInfoMap = new HashMap<>(100);
        if (now.getTime() > System.currentTimeMillis()) {
            return orderInfoMap;
        }
        if (StringUtils.isBlank(orderScene)) {
            orderScene = "3";
        }
        if (StringUtils.isBlank(orderCountType)) {
            orderCountType = "1";
        }
        Map<String, String> orderParamJson = getOrderParamJson(pageNo, now, orderScene, orderCountType, span);
        logger.info("getOrderInfoMap--> param: " + orderParamJson);
        String result = TbkUtil.request("taobao.tbk.order.get", getVersion(), orderParamJson);
        logger.debug("getOrderInfoMap--> res: " + result);
        Map<String, Object> response = JSONUtil.parseObj(result);
        boolean rightResponse = response.containsKey("tbk_order_get_response");
        if (rightResponse) {
            response = (Map<String, Object>) response.get("tbk_order_get_response");
            response = (Map<String, Object>) response.get("results");
            List<Map<String, Object>> resList = (List<Map<String, Object>>) response.get("n_tbk_order");
            if (resList != null && resList.size() > 0) {
                logger.info("getOrderInfoMap--> orderSize: " + resList.size());
                for (Map<String, Object> res : resList) {
                    String orderId = res.get("trade_id").toString();
                    res.put("orderScene", orderScene);
                    res.put("orderCountType", orderCountType);
                    orderInfoMap.put(orderId, res);
                }
                boolean hasNextPage = resList.size() >= 100;
                if (hasNextPage) {
                    logger.info("getOrderInfoMap--> hasNext: " + pageNo);
                    Map<String, Map<String, Object>> nextMap = getOrderInfoMap(now, pageNo + 1);
                    orderInfoMap.putAll(nextMap);
                }
            } else {
                logger.info("getOrderInfoMap--> res size 0! " + pageNo + ":" + result);
            }
        } else {
            logger.info("getOrderInfoMap--> fail: " + result);
        }
        return orderInfoMap;
    }

    private static Map<String, String> getOrderParamJson(int pageIndex, Date now, String orderScene, String orderCountType, String span) {
        if (StringUtils.isBlank(span)) {
            span = "1200";
        }
        String time = DateUtil.now();
        Map<String, String> filter = new HashMap<>(3);
        filter.put("fields", "special_id,relation_id,trade_parent_id,trade_id,num_iid,item_title,item_num,price,pay_price,commission,commission_rate,create_time,earning_time,tk_status,pub_share_pre_fee,income_rate,site_id,adzone_id,total_commission_rate,total_commission_fee,click_time,auction_category");
        filter.put("start_time", time);
        filter.put("span", span);
        filter.put("page_no", pageIndex + "");
        filter.put("page_size", "100");
        filter.put("order_query_type", "create_time");
        filter.put("order_scene", orderScene);
        filter.put("order_count_type", orderCountType);
        return filter;
    }
    /*
        taobao.tbk.order.get
        {
          "start_time": "2019-03-13 16:22:16",
          "fields": "special_id,relation_id,trade_parent_id,trade_id,num_iid,item_title,item_num,price,pay_price,commission,commission_rate,create_time,earning_time,tk_status,pub_share_pre_fee,income_rate,site_id,adzone_id,total_commission_rate,total_commission_fee,click_time,auction_category",
          "order_query_type": "create_time",
          "order_scene": "2"
        }
     */

    public static String getShopUrl(String shopId) throws Exception {
        return getShopUrl(shopId, null);
    }
    public static String getShopUrl(String shopId, String adzoneId) throws Exception {
        getResource();
        if (shopId == null) {
            return null;
        }
        if (adzoneId == null) {
            adzoneId = zone;
        }
        Map<String, String> params = new HashMap<>(3);
        params.put("fields", "num_iid,click_url");
        params.put("num_iids", shopId);
        params.put("adzone_id", adzoneId);
        String result = request("taobao.tbk.item.convert", params);
        Map<String, Object> response = JSONUtil.parseObj(result);
        boolean rightResponse = response.containsKey("tbk_item_convert_response");
        if (rightResponse) {
            response = (Map<String, Object>) response.get("tbk_item_convert_response");
            response = (Map<String, Object>) response.get("results");
            List<Map<String, Object>> resList = (List<Map<String, Object>>) response.get("n_tbk_item");
            if (resList != null && resList.size() > 0) {
                logger.debug("getShopUrl--> size: " + resList.size());
                for (Map<String, Object> res : resList) {
                    if (res.get("num_iid").toString().equals(shopId)) {
                        return res.get("click_url").toString();
                    }
                }
            } else {
                logger.info("getShopUrl--> size 0: " + result);
            }
        }
        return null;
    }

    public static Map<String, Object> getCouponInfo(String shopId) throws Exception {
        return getCouponInfo(shopId, null);
    }
    public static Map<String, Object> getCouponInfo(String shopId, String adzoneId) throws Exception {
        getResource();
        if (shopId == null) {
            return null;
        }
        if (adzoneId == null) {
            adzoneId = zone;
        }
        Map<String, String> params = new HashMap<>(3);
        params.put("platform", "2");
        params.put("item_id", shopId);
        params.put("adzone_id", adzoneId);
        String result = request("taobao.tbk.coupon.convert", params);
        Map<String, Object> response = JSONUtil.parseObj(result);
        boolean rightResponse = response.containsKey("tbk_coupon_convert_response");
        if (rightResponse) {
            response = (Map<String, Object>) response.get("tbk_coupon_convert_response");
            response = (Map<String, Object>) response.get("result");
            return (Map<String, Object>) response.get("results");
        } else {
            logger.error("getCouponInfo--> error: " + result);
        }
        return null;
    }

    public static Map<String, Map<String, Object>> getShopInfoMap(String[] skuIds) throws Exception {
        getResource();
        Map<String, Map<String, Object>> skuInfoMap = new HashMap<>(10);
        if (skuIds == null || skuIds.length == 0) {
            return null;
        }
        String skuIdStr =  StringUtils.join(skuIds, ",");
        Map<String, String> params = new HashMap<>(3);
        params.put("platform", "2");
        params.put("num_iids", skuIdStr);
        try {
            String result = request("taobao.tbk.item.info.get", params);
            Map<String, Object> response =  JSONUtil.parseObj(result);
            boolean rightResponse = response.containsKey("tbk_item_info_get_response");
            if (rightResponse) {
                response = (Map<String, Object>) response.get("tbk_item_info_get_response");
                response = (Map<String, Object>) response.get("results");
                List<Map<String, Object>> resList = (List<Map<String, Object>>) response.get("n_tbk_item");
                if (resList != null && resList.size() > 0) {
                    for (Map<String, Object> shopInfo : resList) {
                        String skuId = shopInfo.get("num_iid").toString();
                        skuInfoMap.put(skuId, shopInfo);
                    }
                    if (resList.size() != skuIds.length) {
                        logger.error("getShopInfoMap--> size not right: have/need " + +resList.size() + " :: " + skuIds.length);
                    }
                } else {
                    logger.info("getShopInfoMap--> size 0: " + result);
                }
            }
        } catch (Exception custom) {
            logger.error("getShopInfoMap--> error: " + custom.getMessage());
        }
        return skuInfoMap;
    }

    public static Map<String, Object> getShopInfo(String skuId) throws Exception {
        Map<String, Map<String, Object>> shopInfoMap = getShopInfoMap(new String[]{skuId});
        if (shopInfoMap != null && shopInfoMap.containsKey(skuId)) {
            return shopInfoMap.get(skuId);
        } else {
            logger.error("getShopInfo--> no result");
            return null;
        }
    }

    public static String generatorCode(String text, String logo, String url) throws Exception {
        return generatorCode(text, logo, url, null);
    }
    public static String generatorCode(String text, String logo, String url, String rid) throws Exception {
        getResource();
        if (url == null) {
            return null;
        }
        if (logo == null) {
            logo = "default pic";
        }
        if (text == null) {
            text = "我分享了一个淘口令给你, 打开看看吧!";
        }
        if (rid != null) {
            boolean hasParamSeparator = url.contains("?");
            if (hasParamSeparator) {
                url += "&relationId=" + rid;
            } else {
                url += "?relationId=" + rid;
            }
        }

        Map<String, String> params = new HashMap<>(3);
        params.put("text", text);
        params.put("url", url);
        params.put("logo", logo);
        String result = request("taobao.tbk.tpwd.create", params);
        Map<String, Object> response = JSONUtil.parseObj(result);
        boolean rightResponse = response.containsKey("tbk_tpwd_create_response");
        if (rightResponse) {
            response = (Map<String, Object>) response.get("tbk_tpwd_create_response");
            response = (Map<String, Object>) response.get("data");
            return response.get("model").toString();
        } else {
            logger.error("generatorCode--> error: " + result);
        }
        return null;
    }

    public static String convertCode(String code, boolean returnId) throws Exception {
        return convertCode(code, returnId, null);
    }
    public static String convertCode(String code, boolean returnId, String zoneId) throws Exception {
        getResource();
        if (code == null) {
            return null;
        }
        if (zoneId == null) {
            zoneId = zone;
        }
        Map<String, String> params = new HashMap<>(3);
        params.put("password_content", code);
        params.put("adzone_id", zoneId);
        String result = request("taobao.tbk.tpwd.convert", params);
        Map<String, Object> response = JSONUtil.parseObj(result);
        boolean rightResponse = response.containsKey("tbk_tpwd_convert_response");
        if (rightResponse) {
            response = (Map<String, Object>) response.get("tbk_tpwd_convert_response");
            response = (Map<String, Object>) response.get("data");
            if (returnId) {
                return response.get("num_iid").toString();
            } else {
                return response.get("click_url").toString();
            }
        } else {
            logger.error("convertCode--> error: " + result);
        }
        return null;
    }

    public static String bindSpId(String code, String session) throws Exception {
        getResource();
        if (code == null) {
            return null;
        }
        if (session == null) {
            return null;
        }
        Map<String, String> params = new HashMap<>(3);
        params.put("inviter_code", code);
        params.put("info_type", "1");
        params.put("session", session);
        String result = request("taobao.tbk.sc.publisher.info.save", params);
        Map<String, Object> response =  JSONUtil.parseObj(result);
        boolean rightResponse = response.containsKey("tbk_sc_publisher_info_save_response");
        if (rightResponse) {
            response = (Map<String, Object>) response.get("tbk_sc_publisher_info_save_response");
            response = (Map<String, Object>) response.get("data");
            boolean hasSpId = response.containsKey("special_id") && response.get("special_id") != null;
            if (hasSpId) {
                return response.get("special_id").toString();
            } else {
                logger.error("bindSpId--> error: " + result);
            }
        } else {
            logger.error("bindSpId--> error: " + result);
        }
        return null;
    }

    public static String bindRId(String code, String session) throws Exception {
        getResource();
        if (code == null) {
            return null;
        }
        if (session == null) {
            return null;
        }
        Map<String, String> params = new HashMap<>(3);
        params.put("inviter_code", code);
        params.put("info_type", "1");
        params.put("session", session);
        String result = request("taobao.tbk.sc.publisher.info.save", params);
        Map<String, Object> response =  JSONUtil.parseObj(result);
        boolean rightResponse = response.containsKey("tbk_sc_publisher_info_save_response");
        if (rightResponse) {
            response = (Map<String, Object>) response.get("tbk_sc_publisher_info_save_response");
            response = (Map<String, Object>) response.get("data");
            boolean hasRId = response.containsKey("relation_id") && response.get("relation_id") != null;
            if (hasRId) {
                return response.get("relation_id").toString();
            } else {
                logger.error("bindRId--> error: " + result);
            }
        } else {
            logger.error("bindRId--> error: " + result);
        }
        return null;
    }

    public static Map<String, Object> getReturnOrderInfo(PayOrderModel orderModel, String thirdOrder, String bizType) throws Exception {
        if (orderModel == null) {
            return null;
        }
        if (StringUtils.isBlank(thirdOrder)) {
            thirdOrder = "1";
        }
        if (StringUtils.isBlank(bizType)) {
            bizType = "2";
        }
        String returnOrderQueryTime = orderModel.getFinishDate();
        logger.debug("getReturnOrderInfo--> in: " + returnOrderQueryTime + " :: " + thirdOrder);
        Map<String, String> params = new HashMap<>(1);
        params.put("search_option", "{\"page_size\": 100, \"search_type\": 2, \"refund_type\": " + thirdOrder + ", \"start_time\": \"" + returnOrderQueryTime + "\", \"page_no\": 1, \"biz_type\": "+bizType+"}");
        String result = TbkUtil.request("taobao.tbk.relation.refund", params);
        Map<String, Object> response = JSONUtil.parseObj(result);
        boolean rightResponse = response.containsKey("tbk_relation_refund_response");
        if (rightResponse) {
            response = (Map<String, Object>) response.get("tbk_relation_refund_response");
            response = (Map<String, Object>) response.get("result");
            boolean rightResultCode = response.containsKey("result_code") && response.get("result_code") != null && "200".equals(response.get("result_code").toString());
            if (rightResultCode) {
                response = (Map<String, Object>) response.get("data");
                response = (Map<String, Object>) response.get("results");
                List<Map<String, Object>> returnOrderList = (List<Map<String, Object>>) response.get("result");
                if (returnOrderList != null && returnOrderList.size() > 0) {
                    for (Map<String, Object> returnOrder : returnOrderList) {
                        String tbTradeId = returnOrder.get("tb_trade_id").toString();
                        String orderNo = orderModel.getOrderNo();
                        if (tbTradeId.equals(orderNo)) {
                            return returnOrder;
                        }
                    }
                } else {
                    logger.debug("getReturnOrderInfo--> size 0");
                }
            } else {
                logger.error("getReturnOrderInfo--> error: " + result);
            }
        } else {
            logger.error("getReturnOrderInfo--> error: " + result);
        }
        logger.error("getReturnOrderInfo--> orderNo: no return info: " + orderModel.getOrderNo());
        return null;
    }
}